package com.base.keyvalue.store.impl.disk;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import org.apache.commons.io.IOUtils;

import com.base.keyvalue.store.Configure;
import com.base.keyvalue.store.Storage;
import com.base.keyvalue.utils.RuntimeExceptionWraper;

/**
 * @author yanjiaqiang
 * @date 2011-3-21
 * @version $Id: DiskHashStorage.java 7 2011-07-15 04:24:04Z JiaqiangYan@gmail.com $
 */
public class DiskHashStorage implements Storage {
	MemeoryIndexStore indexStore = new MemeoryIndexStore();
	DataStore dataStore = new DataStore();

	@Override
	public byte[] put(String key, byte[] data) {
		int fileIndex = dataStore.getCurrentFileIndex();
		int filePosOffset = dataStore.getCurrentFilePosOffset();
		IndexEntry index = new IndexEntry();
		index.setDatafileIndex(fileIndex);
		index.setDatafilePosOffset(filePosOffset);

		indexStore.put(key, index);
		DataEntry de = new DataEntry(data);
		dataStore.put(de);
		return data;

	}

	@Override
	public int size() {
		return indexStore.size();
	}

	@Override
	public boolean isEmpty() {
		return size() <= 0;
	}

	@Override
	public boolean containsKey(Object key) {
		return indexStore.containsKey(key);
	}

	@Override
	public boolean containsValue(Object value) {
		throw new UnsupportedOperationException();
	}

	@Override
	public byte[] get(Object key) {
		IndexEntry index = indexStore.get(key);
		if (index == null) {
			return null;
		}
		DataEntry dataEntry = dataStore.get(index.getDatafileIndex(), index.getDatafilePosOffset());
		if (dataEntry == null) {
			throw RuntimeExceptionWraper.wrap("数据错误 " + index.toString());
		}
		return dataEntry.getData();
	}

	@Override
	public byte[] remove(Object key) {
		IndexEntry index = indexStore.remove(key);
		if (index != null) {
			return dataStore.remove(index.getDatafileIndex(), index.getDatafilePosOffset());
		}
		return null;
	}

	@Override
	public void putAll(Map<? extends String, ? extends byte[]> m) {
		for (Map.Entry<? extends String, ? extends byte[]> entry : m.entrySet()) {
			put(entry.getKey(), entry.getValue());
		}
	}

	@Override
	public void clear() {
		throw new UnsupportedOperationException();
	}

	@Override
	public Set<String> keySet() {
		return indexStore.keySet();
	}

	@Override
	public Collection<byte[]> values() {
		throw new UnsupportedOperationException();
	}

	@Override
	public Set<java.util.Map.Entry<String, byte[]>> entrySet() {
		throw new UnsupportedOperationException();
	}

	@Override
	public byte[] putIfAbent(String k, byte[] v) {
		if (containsKey(k)) {
			return get(k);
		}
		return put(k, v);
	}

	@Override
	public void close() {
		dataStore.close();
		indexStore.close();
	}

	static class MemeoryIndexStore extends AbstractMapDecorator<String, IndexEntry> {

		static Map<String, IndexEntry> map = null;
		static String indexfilePath = Configure.getDataBaseDir() + File.separator + "index"
				+ Configure.INDEX_FILE_SUFFIX;

		public MemeoryIndexStore() {
			super(map = init());
		}

		public void close() {

			ObjectOutputStream objStream = null;
			try {
				objStream = new ObjectOutputStream(new FileOutputStream(indexfilePath));
				objStream.writeObject(map);
				objStream.close();
			} catch (Exception e) {
				RuntimeExceptionWraper.wrap(e);
			} finally {
				IOUtils.closeQuietly(objStream);
			}
		}

		public static Map<String, IndexEntry> init() {
			ObjectInputStream objStream = null;
			Map<String, IndexEntry> map = null;
			try {
				if (new File(indexfilePath).exists()) {
					objStream = new ObjectInputStream(new FileInputStream(indexfilePath));
					map = (Map<String, IndexEntry>) objStream.readObject();
				}
				if (map == null) {
					map = new HashMap<String, IndexEntry>();
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
			return map;
		}
	}
}
